import { createEmpty, getTopLeft, getWidth } from "ol/extent";
import * as Layer from "ol/layer";
import { get } from "ol/proj";
import * as Source from "ol/source";
import WMTSTileGrid from "ol/tilegrid/WMTS";
import TileState from "ol/TileState";
import * as keys from "./keys";
import { log } from "./log";
import { getMap } from "./instance";
import type { RenderWMSLayerOptions, RenderWMTSLayerOptions } from "./types";
import { _fetchTileByBlob } from "./utils";
import BUTILS from "../../universal-utils-browser";

/**
 * @description: 渲染 WMS 瓦片图层
 * @private
 * @param {RenderWMSLayerOptions} options 配置项
 * @returns {Layer.Vector}
 */
export const renderWMSTileLayer = (
  options: RenderWMSLayerOptions
): Layer.Tile => {
  const mapInstance = getMap();
  if (!mapInstance) {
    throw new Error("地图实例未初始化");
  }
  const {
    attributions,
    attributionsCollapsible,
    crossOrigin = "anonymous",
    headers,
    params = {},
    name,
    style = "default",
    type: layerType,
    url,
    visible = true,
  } = options;
  let layer = mapInstance
    .getAllLayers()
    .find((item: any) => item.get(keys.LAYER_TYPE_KEY) === layerType) as
    | Layer.Tile
    | undefined;
  if (!(layer instanceof Layer.Tile)) {
    console.log("----------------", options);
    layer = new Layer.Tile({
      ...BUTILS.Object.excludeProperties(options, [
        "attributions",
        "attributionsCollapsible",
        "name",
        "crossOrigin",
        "headers",
        "params",
        "style",
        "type",
        "url",
      ]),
    });
    layer.set(keys.LAYER_TYPE_KEY, layerType);
    layer.set(keys.LAYER_FORMAT_KEY, "wms");
    mapInstance.addLayer(layer);
  }

  log("Render WMS Layer", options);

  layer.setSource(
    new Source.TileWMS({
      attributions,
      attributionsCollapsible,
      crossOrigin,
      params: { LAYERS: name, STYLES: style, TILED: true, ...params },
      tileLoadFunction: (tile, src) => {
        tile.setState(TileState.LOADING);
        _fetchTileByBlob({ url: src, headers })
          .then((blob) => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            const image = tile.getImage() as InstanceType<typeof Image>;
            if (image instanceof Image) {
              const _url = URL.createObjectURL(blob);
              image.src = _url;
              image.onload = () => {
                URL.revokeObjectURL(_url);
                tile.setState(TileState.LOADED);
              };
            }
          })
          .catch((e) => {
            tile.setState(TileState.ERROR);
            throw e;
          });
      },
      url,
      wrapX: true,
    })
  );
  layer.setVisible(visible);

  return layer;
};

/**
 * @description: 显示或渲染 WMS 瓦片图层
 * @private
 * @param {RenderWMSLayerOptions} options 配置项
 * @returns {Layer.Vector}
 */
export const showOrRenderWMSTileLayer = (
  options: RenderWMSLayerOptions
): Layer.Tile => {
  const mapInstance = getMap();
  if (!mapInstance) {
    throw new Error("地图实例未初始化");
  }
  const { type: layerType, visible = true } = options;
  const layer = mapInstance
    .getAllLayers()
    .find((item) => item.get(keys.LAYER_TYPE_KEY) === layerType) as
    | Layer.Tile
    | undefined;
  if (layer instanceof Layer.Tile) {
    log("Change WMS Layer Visible", options);
    layer.setVisible(visible);
    return layer;
  }

  return renderWMSTileLayer(options);
};

/**
 * @description: 渲染 WMTS 瓦片图层
 * @private
 * @param {RenderWMTSLayerOptions} options 配置项
 * @returns {Layer.Vector}
 */
export const renderWMTSTileLayer = (
  options: RenderWMTSLayerOptions
): Layer.Tile => {
  const mapInstance = getMap();
  if (!mapInstance) {
    throw new Error("地图实例未初始化");
  }
  const {
    attributions,
    attributionsCollapsible,
    crossOrigin = "anonymous",
    format,
    headers,
    matrixSet,
    name,
    projection = "EPSG:4326",
    style = "default",
    type: layerType,
    url,
    visible = true,
  } = options;
  let layer = mapInstance
    .getAllLayers()
    .find((item) => item.get(keys.LAYER_TYPE_KEY) === layerType) as
    | Layer.Tile
    | undefined;
  if (!(layer instanceof Layer.Tile)) {
     console.log("========================", options);
    layer = new Layer.Tile({
      ...BUTILS.Object.excludeProperties(options, [
        "attributions",
        "attributionsCollapsible",
        "crossOrigin",
        "format",
        "headers",
        "matrixSet",
        "name",
        "params",
        "style",
        "type",
        "url",
      ]),
    });
    layer.set(keys.LAYER_TYPE_KEY, layerType);
    layer.set(keys.LAYER_FORMAT_KEY, "wmts");
    mapInstance.addLayer(layer);
  }

  const projectionExtent = get(projection)?.getExtent() || createEmpty();
  const size = getWidth(projectionExtent) / 256;
  const resolutions = new Array(19);
  const matrixIds = new Array(19);
  for (let z = 1; z < 19; ++z) {
    // generate resolutions and matrixIds arrays for this WMTS
    resolutions[z] = size / Math.pow(2, z);
    matrixIds[z] = z;
  }

  log("Render WMTS Layer", options);

  layer.setSource(
    new Source.WMTS({
      attributions,
      attributionsCollapsible,
      crossOrigin,
      layer: name,
      style,
      tileGrid: new WMTSTileGrid({
        origin: getTopLeft(projectionExtent),
        resolutions,
        matrixIds,
      }),
      tileLoadFunction: (tile, src) => {
        tile.setState(TileState.LOADING);
        _fetchTileByBlob({ url: src, headers })
          .then((blob) => {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            const image = tile.getImage() as InstanceType<typeof Image>;
            if (image instanceof Image) {
              const _url = URL.createObjectURL(blob);
              image.src = _url;
              image.onload = () => {
                URL.revokeObjectURL(_url);
                tile.setState(TileState.LOADED);
              };
            }
          })
          .catch((e) => {
            tile.setState(TileState.ERROR);
            throw e;
          });
      },
      format,
      matrixSet,
      url,
      wrapX: true,
    })
  );
  layer.setVisible(visible);

  return layer;
};

/**
 * @description: 显示或渲染 WMTS 瓦片图层
 * @private
 * @param {RenderWMTSLayerOptions} options 配置项
 * @returns {Layer.Vector}
 */
export const showOrRenderWMTSTileLayer = (
  options: RenderWMTSLayerOptions
): Layer.Tile => {
  const mapInstance = getMap();
  if (!mapInstance) {
    throw new Error("地图实例未初始化");
  }
  const { type: layerType, visible = true } = options;
  const layer = mapInstance
    .getAllLayers()
    .find((item) => item.get(keys.LAYER_TYPE_KEY) === layerType) as
    | Layer.Tile
    | undefined;
  if (layer instanceof Layer.Tile) {
    log("Change WMTS Layer Visible", options);
    layer.setVisible(visible);
    return layer;
  }

  return renderWMTSTileLayer(options);
};
